import CANNON from "cannon";
import RescacheMgr from "../../framework/inc/rescachemgr";
import StagerMgr from "../../framework/inc/stagemgr";
import { LV_CONFIG, OBSTACLE_LINE_CONFIG, OBSTACLE_CELL_CONFIG } from "./lvconfig";
import { DEBUG } from "../../relconf";
import EffStar from "../component/effstar";
import EffGround from "../component/effground";

class RigidMeshMgr {
    constructor() {
        this._dynamic_meshlist = [];
        this._dynamic_bodylist = [];
        this._static_meshlist = [];
        this._static_bodylist = [];
    }

    static getInstance() {
        if (!this._inc) {
            this._inc = new RigidMeshMgr();
        }
        return this._inc;
    }

    bind(mesh, body, isdynamic) {
        if (!isdynamic) {
            this._static_meshlist.push(mesh);
            this._static_bodylist.push(body);
        }
        else {
            this._dynamic_meshlist.push(mesh);
            this._dynamic_bodylist.push(body);
        }
    }

    update() {
        for (let i = 0; i < this._dynamic_meshlist.length; i++) {
            console.log("no dynamic!");
            const element = this._dynamic_meshlist[i];
            element.position.copy(this._dynamic_bodylist[i].position);
            element.quaternion.copy(this._dynamic_bodylist[i].quaternion);
        }
        let w_pos = new THREE.Vector3();
        for (let i = 0; i < this._static_meshlist.length; i++) {
            const element = this._static_meshlist[i];
            element.getWorldPosition(w_pos)
            this._static_bodylist[i].position.copy(w_pos);
            this._static_bodylist[i].quaternion.copy(element.quaternion);
        }
    }
}

/* =============land============= */
const LAND_LINECNT = 5;               //每片格子的行数
const CELL_DIS = 4;                   //障碍物横向间隔
const LINE_DIS = 7;                   //障碍物纵向基础间隔
const LINE_RANDOM_DIS = 3;            //障碍物纵向随机间隔
/* =============land============= */


/* =============全局============= */
const VIEW_LANDCNT = 3;               //地板格数量
const SCROLL_MAX_SPEED = 100 / 1000;  //跑步速度
const RUNNING_SPEED = 100 / 1000;     //横向速度
/* =============全局============= */

const WORLD_STATU = {
    "IDLE": 0,
    "RUNNING": 1
}

class Terrain extends THREE.Object3D {
    constructor(width, height) {
        super();
        let inc = RescacheMgr.getInstance();
        const grange = 0.2;
        let effground = new EffGround(
            width,
            height,
            grange,
            {
                texture1: inc.getTexture("ground_texture1"),
                texture2: inc.getTexture("ground_texture2"),
                texture3: inc.getTexture("ground_texture3"),
                heightmap: inc.getTexture("ground_heightmap"),
                texturemap: inc.getTexture("ground_texturemap"),
            }
        );
        this.add(effground);
    }
}

class ObstacleLand extends THREE.Object3D {
    constructor(phyworld, lvconf) {
        super();
        this._world = phyworld;
        this._init(lvconf);
    }

    _crt_cell(obsconf, vec3) {
        let size = obsconf["size"] || [1, 1, 1];
        let bodytype = obsconf["bodytype"] || "box";
        let sphere_shape;
        switch (bodytype) {
            case "box":
                sphere_shape = new CANNON.Box(new CANNON.Vec3(size[0], size[1], size[2]));
                break;

            case "circle":
                sphere_shape = new CANNON.Sphere(size[0]);
                break;

            default:
                sphere_shape = new CANNON.Box(new CANNON.Vec3(size[0], size[1], size[2]));
                break;
        }
        let sphere_body = new CANNON.Body({
            mass: 5,
            position: vec3,
            type: CANNON.Body.STATIC,
            shape: sphere_shape
        });
        this._world.add(sphere_body);
        let mesh_box = RescacheMgr.getInstance().cloneGLTF(obsconf["graph"]);
        RigidMeshMgr.getInstance().bind(mesh_box, sphere_body);
        return mesh_box
    }

    _crt_line(linenum) {
        const line = new THREE.Object3D();
        const lineconf = OBSTACLE_LINE_CONFIG[linenum];
        const linecnt = lineconf.length;
        let cellid;
        let obs;
        let posx, posz;
        for (const obsidx in lineconf) {
            if (lineconf.hasOwnProperty(obsidx)) {
                cellid = lineconf[obsidx];
                if (cellid) {
                    const obsconf = OBSTACLE_CELL_CONFIG[cellid];
                    posx = CELL_DIS * (obsidx - (linecnt - 1) / 2) + (-LINE_RANDOM_DIS + Math.random() * LINE_RANDOM_DIS * 2);
                    posz = -LINE_DIS / 2 + Math.random() * LINE_DIS;
                    obs = this._crt_cell(obsconf, new CANNON.Vec3(0, 0, 0));
                    obs.position.x = posx;
                    line.add(obs);
                }
            }
        }
        return line;
    }

    _init(lvconf) {
        const land_conf = lvconf;
        let minidx, maxid;
        let lindid;
        let lineobj;
        for (let i = 0; i < LAND_LINECNT; i++) {
            minidx = land_conf["min"];
            maxid = land_conf["max"];
            // lind
            lindid = minidx + Math.floor(Math.random() * (maxid - minidx + 1));
            lineobj = this._crt_line(lindid);
            lineobj.position.z = -(LINE_DIS * i);
            this.add(lineobj);
        }
        const width = CELL_DIS * 7, height = LINE_DIS * LAND_LINECNT;
        let obj_terain = new Terrain(width, height);
        this.add(obj_terain);
        // if (DEBUG) {
        //     let geometry = new THREE.PlaneGeometry(width, height, 1, 1);
        //     let material = new THREE.MeshBasicMaterial({ color: Math.random() * 0xffff00, side: THREE.DoubleSide });
        //     let plane = new THREE.Mesh(geometry, material);
        //     plane.rotation.x = Math.PI / 2;
        //     // plane.position.z = -6;
        //     this.add(plane);
        // }
    }

    get l_length() {
        return LINE_DIS * LAND_LINECNT;
    }
}

class ObstacleMgr {
    constructor(world, resmgr) {
        this._world = world;
        this._res_mgr = resmgr;
        this._mlands = [];
        this._nspeed = 0;
    }

    newLand(lv) {
        const lvconf = LV_CONFIG["lv_" + lv.toString()];
        let obj_land = new ObstacleLand(this._world, lvconf);
        this._mlands.push(obj_land);
        return obj_land;
    }

    scrollLand() {
        for (const landidx in this._mlands) {
            if (this._mlands.hasOwnProperty(landidx)) {
                const element = this._mlands[landidx];
                this._nspeed = this._nspeed > SCROLL_MAX_SPEED ? SCROLL_MAX_SPEED : this._nspeed + 0.001;
                element.position.z += this._nspeed;
                if ((element.position.z) >= LINE_DIS * LAND_LINECNT) {
                    element.position.z = - element.l_length * (VIEW_LANDCNT - 1);
                }
            }
        }
    }
}


let touch_sx, touch_sy;
let mv_nx, mv_ny;
export default class PhyWorld {
    constructor(scene, camera) {
        this._scene = scene;
        this._camera = camera;

        this._g_statu = WORLD_STATU['IDLE'];
        this._player = undefined;
        this._dir_speed = 0;

        this._world = this._init_phy();
    }

    static getWorld(scene, camera) {
        if (!this._inc && scene && camera) {
            this._inc = new PhyWorld(scene, camera);
        }
        return this._inc;
    }

    _init_env() {
        //fog
        let fog = new THREE.Fog(0xffffff, 1, 100);
        this._scene.fog = fog;

        //skybox 
        const tplist = [
            "res/texture/skybox/purplenebula_lf.png", // left
            "res/texture/skybox/purplenebula_rt.png", // right
            "res/texture/skybox/purplenebula_up.png", // top
            "res/texture/skybox/purplenebula_dn.png", // bottom
            "res/texture/skybox/purplenebula_bk.png", // back
            "res/texture/skybox/purplenebula_ft.png"  // front
        ];
        let skybox = d.skybox(tplist);
        this._scene.background = skybox;
        //effstar 
        let inc = RescacheMgr.getInstance()
        let effstar = new EffStar(
            inc.getTexture("spark1"), 20
        );
        console.log("add start") 
        console.log("add start") 
        console.log("add start") 
        console.log("add start") 
        console.log("add start") 
        this._scene.add(effstar);
        this._effstar = effstar;
    }

    _init_phy() {
        //world
        let world = new CANNON.World();
        world.gravity.set(0, 10, 0);
        world.broadphase = new CANNON.NaiveBroadphase();
        this._world = world;
        this._crt_baseenv();

        this._res_mgr = RescacheMgr.getInstance();

        this._init_env();
        this._init_landpool(1);
        this._add_player(new CANNON.Vec3(0, 0.5, -1))
        // this.startRunning();

        //show start 
        StagerMgr.getInstance().showView("viewstart");
        return world;
    }

    _init_player() {
    }

    get world() {
        return this._world;
    }

    reLoad() {
        this._g_statu = WORLD_STATU["IDLE"];
    }

    startRunning() {
        this._g_statu = WORLD_STATU["RUNNING"];
    }

    _init_landpool(lvnum) {
        if (!this._obs_mgr) {
            this._obs_mgr = new ObstacleMgr(this._world, this._res_mgr);
        }
        let landobj;
        for (let i = 0; i < VIEW_LANDCNT; i++) {
            landobj = this._obs_mgr.newLand(lvnum);
            landobj.position.z = - landobj.l_length * i;
            this._scene.add(landobj);
        }
    }

    _crt_baseenv() {
        let ground_shape = new CANNON.Plane();
        let ground_body = new CANNON.Body({
            mass: 0,
            shape: ground_shape
        });
        ground_body.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 180 * 90)
        this._world.add(ground_body);
    }

    _add_player(vec3) {
        let sphere_shape = new CANNON.Box(new CANNON.Vec3(0.5, 1, 0.5));
        let sphere_body = new CANNON.Body({
            mass: 5,
            position: vec3,
            shape: sphere_shape,
            type: CANNON.Body.DYNAMIC
        });
        this._world.add(sphere_body);

        let mesh_player = this._res_mgr.cloneGLTF("box");

        this._scene.add(mesh_player);
        sphere_body.addEventListener("collide", (e) => {
            // this.reLoad();
        });
        RigidMeshMgr.getInstance().bind(mesh_player, sphere_body);
        this._player = mesh_player;
    }

    _async_phypos() {

    }

    _chk_movedirec(x, y) {
        if (!x && !y) this._dir_speed = 0;
        if (x > d.w / 2) {
            this._dir_speed = 1;
        }
        else {
            this._dir_speed = -1;
        }
    }

    _touchstar(params) {
        let evt = params[0];
        touch_sx = evt.touches[0].pageX, touch_sy = evt.touches[0].pageY;
        this._chk_movedirec(touch_sx, touch_sy);
    }

    _touchmove(params) {
        let evt = params[0];
        mv_nx = evt.targetTouches[0].pageX, mv_ny = evt.targetTouches[0].pageY;
        this._chk_movedirec(mv_nx, mv_ny);

        /* 
                检查滑动方向-
        
                mv_disx = mv_nx - touch_sx, mv_disy = mv_ny - touch_sy;
                if (Math.abs(mv_disx) > Math.abs(mv_disy)) {
                    if (mv_disx > 0) {
                        console.log("move right");
                        camera.position.x += 0.1;
                        if (this._player) {
                            this._player.position.x += 0.1;
                        }
                    }
                    else {
                        console.log("move left");
                        camera.position.x -= 0.1;
                        if (this._player) {
                            this._player.position.x -= 0.1;
                        }
                    }
                }
                else {
                    if (mv_disy > 0) {
                        console.log('move down');
                    }
                    else {
                        console.log("move up");
                    }
                } 
                
                */
    }

    _touchend(params) {
        let evt = params[0];
        this._dir_speed = 0;
    }

    update() {
        //running man 
        if (this._player) {
            this._player.position.x += this._dir_speed * RUNNING_SPEED;
            this._camera.position.x = this._player.position.x;
        }
        if (this._effstar) {
            this._effstar.update();
        }
        RigidMeshMgr.getInstance().update();
        if (this._g_statu == WORLD_STATU["RUNNING"]) {
            this._obs_mgr.scrollLand();
        }
    }
}